using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
using System.Web;

namespace CKS.EBE.API
{
	/// <summary>Handles requests for dynamic images from the ImageHipChallenge control.</summary>
	public class ImageHipChallengeHandler : IHttpHandler
	{
		#region QueryString Parameters
		private static Guid Id
		{
			get { return new Guid(HttpContext.Current.Request.QueryString["id"]); }
		}

		private static int Width
		{
			get { return int.Parse(HttpContext.Current.Request.QueryString["width"]); }
		}

		private static int Height
		{
			get { return int.Parse(HttpContext.Current.Request.QueryString["height"]); }
		}
		#endregion

		/// <summary>Gets whether this handler is reusable.</summary>
		/// <remarks>This handler is not thread-safe (uses non thread-safe member variables), so it is not reusable.</remarks>
		public bool IsReusable { get { return false; } }

		/// <summary>Processes the image request and generates the appropriate image.</summary>
		/// <param name="context">The current HttpContext.</param>
		public void ProcessRequest(HttpContext context)
		{
			string text = CaptchaUtility.GetChallengeText(Id);

			if (text == null) return;
			// Change the response headers to output a JPEG image.
			context.Response.ContentType = "image/jpeg";
			context.Response.Cache.SetCacheability(HttpCacheability.NoCache);
			context.Response.BufferOutput = false;

			using (Bitmap bmp = GenerateImage(text, new Size(Width, Height)))
			{
				try
				{
					bmp.Save(context.Response.OutputStream, ImageFormat.Jpeg);
				}
				catch (NotSupportedException)
				{
					// occures if we are theming wiki pages
				}
			}
		}

		/// <summary>Generates the challenge image.</summary>
		/// <param name="text">The text to be rendered into the image.</param>
		/// <param name="size">The size of the image to generate.</param>
		/// <returns>A dynamically-generated challenge image.</returns>
		public Bitmap GenerateImage(string text, Size size)
		{
			// Create the new Bitmap of the specified size and render to it
			Bitmap bmp = new Bitmap(size.Width, size.Height);

			RandomNumbers rand = new RandomNumbers();

			using (Graphics g = Graphics.FromImage(bmp))
			{
				g.SmoothingMode = SmoothingMode.AntiAlias;
				Rectangle rect = new Rectangle(0, 0, size.Width, size.Height);

				// Fill in the background.
				HatchBrush hatchBrush = new HatchBrush(HatchStyle.SmallConfetti, Color.LightGray, Color.White);
				g.FillRectangle(hatchBrush, rect);

				// Set up the text font.
				SizeF sizeF;
				float fontSize = rect.Height + 1;
				Font font;
				// Adjust the font size until the text fits within the image.
				do
				{
					fontSize--;
					font = new Font(FontFamily.GenericSansSerif, fontSize, FontStyle.Bold);
					sizeF = g.MeasureString(text, font);
				} while (sizeF.Width > rect.Width);

				// Set up the text format.
				StringFormat format = new StringFormat();
				format.Alignment = StringAlignment.Center;
				format.LineAlignment = StringAlignment.Center;

				// Create a path using the text and warp it randomly.
				GraphicsPath path = new GraphicsPath();
				path.AddString(text, font.FontFamily, (int)font.Style, font.Size, rect, format);
				const float v = 4F;
				PointF[] points =
					{
						new PointF(rand.Next(rect.Width)/v, rand.Next(rect.Height)/v),
						new PointF(rect.Width - rand.Next(rect.Width)/v, rand.Next(rect.Height)/v),
						new PointF(rand.Next(rect.Width)/v, rect.Height - rand.Next(rect.Height)/v),
						new PointF(rect.Width - rand.Next(rect.Width)/v, rect.Height - rand.Next(rect.Height)/v)
					};

				Matrix matrix = new Matrix();
				matrix.Translate(0F, 0F);
				path.Warp(points, rect, matrix, WarpMode.Perspective, 0F);

				// Draw the text.
				hatchBrush = new HatchBrush(HatchStyle.LargeConfetti, Color.LightGray, Color.DarkGray);
				g.FillPath(hatchBrush, path);

				// Add some random noise.
				int m = Math.Max(rect.Width, rect.Height);
				for (int i = 0; i < (int)(rect.Width * rect.Height / 30F); i++)
				{
					int x = rand.Next(rect.Width);
					int y = rand.Next(rect.Height);
					int w = rand.Next(m / 50);
					int h = rand.Next(m / 50);
					g.FillEllipse(hatchBrush, x, y, w, h);
				}

				// Clean up.
				font.Dispose();
				hatchBrush.Dispose();
			}

			return bmp;
		}
	}
}